home *** CD-ROM | disk | FTP | other *** search
/ BBS Toolkit / BBS Toolkit.iso / maximus / flist21.zip / FLIST.C next >
Text File  |  1992-08-26  |  26KB  |  856 lines

  1. /*
  2.  
  3. FLIST -- Generates file lists based on privilege levels and keys
  4.  
  5. Version 2.1  (8/26/92)
  6.  
  7. Written by Bob Quinlan of Austin, Texas, USA
  8. Sysop of Red October at 512-834-2593 (1:382/111)
  9.  
  10. Copyright 1992 by Bob Quinlan
  11.  
  12. Compatible with Maximus 2.00 and 2.01
  13.  
  14.  
  15. Special thanks to David Powers for encouragement, ideas, and code!
  16.  
  17.  
  18. This program generates a list of files that a user with a particular
  19. privilege level and set of keys could access.  It is intended to make
  20. the procedure independent of changes to the structure of your file
  21. areas.  Instead of specifying which areas should be included you just
  22. specify what sort of user you want the list to apply to.
  23.  
  24. You can also create lists of new files.  Just use /D to specify how many
  25. days of files you want to list.
  26.  
  27.     /Afile      Area file.  You can use this to specify a path to
  28.                 AREA.DAT or to refer to another file (although it must
  29.                 follow the AREA.DAT format).  The filename must be
  30.                 included along with the path.  Defaults to AREA.DAT in
  31.                 the current directory.
  32.  
  33.     /B          Include barricaded areas.  If this switch is not
  34.                 included no barricaded areas will be included.
  35.  
  36.     /C          Include comments from the FILES.BBS file lists.  By
  37.                 default only files and their descriptions are included.
  38.  
  39.                 Using this switch has one side-effect!  Normally area
  40.                 headers are only generated for areas that have files in
  41.                 them.  /C will cause them to be generated for any area
  42.                 that has comments as well.  Do not use /C if you only
  43.                 want areas that have files in them to appear!  (I
  44.                 recommend not using /C with /D so that you don't show
  45.                 lots of apparently empty file areas.)
  46.  
  47.     /Ddays      Days.  Excludes all files more than this many days old.
  48.                 Use this switch to create lists of new files.
  49.  
  50.     /Ffile      Output file.  The pathname of the file you want the file
  51.                 list written to.  Defaults to FLIST.TXT in the current
  52.                 area.
  53.  
  54.     /HAfile     Area header file.  The contents of this file will be
  55.                 added to the report for each area header.  There are
  56.                 several sequences you can embed in the file that have
  57.                 special meanings:
  58.  
  59.                     %N will be replaced with the area name.
  60.                     %I will be replaced with the area information.
  61.                     %D will be replaced with the report date.
  62.                     %T will be replaced with the report time.
  63.                     %A will be replaced with the age of the files
  64.                        included in the report.  Do not use this except
  65.                        with the /D command line parameter!
  66.  
  67.                     Any of these may be followed by an L, R, or C
  68.                     immediately followed by a number.  These will left,
  69.                     right, or center justify the value in a field of the
  70.                     specified width.
  71.  
  72.                     You can also follow any of the above with an NF or
  73.                     with an Fx.  NF stands for "no fill" and has no
  74.                     effect (see examples).  Fx stands for "fill" and
  75.                     will cause the entire field to be replaced by an
  76.                     equivalent number of the character 'x'.  This is
  77.                     useful when drawing boxes.
  78.  
  79.                 This switch is not required.  If no area header file is
  80.                 specified a default header will be used.
  81.  
  82.     /HBfile     Beginning header file.  The contents of this file will
  83.                 be added to the beginning of the report.  There are
  84.                 several sequences you can embed in the file that have
  85.                 special meanings.  All of the codes listed under /HA are
  86.                 allowed except for %N and %I.
  87.  
  88.     /HEfile     End header file.  The contents of this file will be
  89.                 added to the end of the report.  There are several
  90.                 sequences you can embed in the file that have special
  91.                 meanings.  All of the codes listed under /HA are allowed
  92.                 except for %N and %I.
  93.  
  94.     /Kkeys      Keys to use for access to locked areas.  Keys are
  95.                 represented by 1-8 and A-X.  An asterisk (*) turns on
  96.                 all of the keys.  Multiple keys can be specified in one
  97.                 /K argument (such as /K12B).
  98.  
  99.     /N          No-wrap.  Leave long description lines intact.  The
  100.                 default is to wrap them.
  101.  
  102.     /O          Include offline files.  By default files that are listed
  103.                 but not present are not included in the list.
  104.  
  105.     /Ppriv      Privilege to use for access to areas.  Only the first
  106.                 letter of the level name is required.  Legal values are:
  107.                     Twit
  108.                     Disgrace
  109.                     Limited
  110.                     Normal
  111.                     Worthy
  112.                     Privil
  113.                     Favored
  114.                     Extra
  115.                     Clerk
  116.                     AsstSysop
  117.                     Sysop
  118.                     Hidden
  119.                 Defaults to Normal.
  120.  
  121.     /Q          Quiet.  Eliminates the progress display.
  122.  
  123.     /T          Truncate long description lines.  The default is to wrap
  124.                 them.
  125.  
  126. Let me show you an example of how to use the %N and %I parameters in an
  127. area header file (defined with the /HA switch).  The purpose of those
  128. parameters is to let you build boxes that will conform to the size of
  129. the text produced by %N and %I.  Here is an example of a simple header
  130. box using these parameters:
  131.  
  132. ┌──%NF───┬──%IF───┐
  133. │  %NNF  │  %INF  │
  134. └──%NF───┴──%IF───┘
  135.  
  136. Note that the characters following the %NF and %IF look like a part of
  137. the regular text, but are actually the characters that will be used to
  138. fill out the parameters to the desired length.
  139.  
  140. This will expand to something that looks like this:
  141.  
  142. ┌───────┬───────────────────────────────────────────┐
  143. │  MAX  │  Maximus BBS files and related utilities  │
  144. └───────┴───────────────────────────────────────────┘
  145.  
  146. Now you can see why the apparently useless NF modifier can be useful. It
  147. pads out the shorter embedded codes so that they visually match those in
  148. the surrounding lines.  The following would produce exactly the same
  149. result as the previous example, but it is a lot harder to figure out how
  150. everything will match up:
  151.  
  152. ┌──%NF───┬──%IF───┐
  153. │  %N  │  %I  │
  154. └──%NF───┴──%IF───┘
  155.  
  156. Here is an example using fixed width fields.  This also right justifies
  157. the area name and left justifies the area information:
  158.  
  159. ┌─%NR9F──┬─%IL50F──┐
  160. │ %NR9NF │ %IL50NF │
  161. └─%NR9F──┴─%IL50F──┘
  162.  
  163. Which will expand to something that looks like this:
  164.  
  165. ┌───────────┬────────────────────────────────────────────────────┐
  166. │       MAX │ Maximus BBS files and related utilities            │
  167. └───────────┴────────────────────────────────────────────────────┘
  168.  
  169.  
  170. FLIST returns the following ERRORLEVELS:
  171.     0 = success
  172.     1 = bad command line parameter
  173.     2 = unable to open AREA.DAT
  174.     3 = unable to open header file
  175.     4 = unable to open output file
  176.     5 = not enough memory to run
  177.     6 = string overflow processing header
  178.  
  179. NOTICE:  You may use, copy, and distribute this program freely as long
  180. as you insure that both the executable and the documentation (.DOC)
  181. files are included in the distribution package.  The source code does
  182. not need to be included.  You may modify this program and document, so
  183. long as reasonable credit is given to the original author if a
  184. substantial portion of the original remains intact.  The author is not
  185. responsible for any losses which may occur either directly or indirectly
  186. as a result of using this program.
  187.  
  188. This program uses the Maximus structures written by Scott J. Dudley.
  189.  
  190. HISTORY:
  191. Version 2.1   (8/26/92) -- Added the /Q switch to eliminate the progress
  192.                            display.  Now returns different errorlevels
  193.                            for each possible error.  Fixed problem with
  194.                            headers before comments.
  195. Version 2.0   (8/23/92) -- Added more error checking.
  196. Version 1.9   (8/22/92) -- Added a progress display.  Fixed a memory
  197.                            corruption bug.
  198. Version 1.8   (8/20/92) -- Added fixed width fields with left, right,
  199.                            and centered justification for embedded
  200.                            variables.  Changed the previous codes for
  201.                            embedded variables!  Removed built-in help to
  202.                            reduce memory requirements.
  203. Version 1.7   (8/10/92) -- Added embedded variables to the begin and end
  204.                            headers.  Note: This eliminated the automatic
  205.                            file age comment that used to be included for
  206.                            newfile lists.
  207. Version 1.6   (8/10/92) -- Added the /HA option to allow customized area
  208.                            headers in the report.  This is an expansion
  209.                            of an idea and code by David Powers.
  210. Version 1.5   (8/09/92) -- Added the /HB and /HE options to allow
  211.                            customized beginning and end text in the
  212.                            report.  Added /? help option to print the
  213.                            parameter list.  Added a clearer explanation
  214.                            of /C's effect on area headers.
  215. Version 1.4   (8/07/92) -- Added the /C option to include file list
  216.                            comments.  Added code to recognize free
  217.                            switches so they do not show up in the
  218.                            descriptions.  Fixed error if first entry in
  219.                            AREA.DAT did not include a file area.
  220. Version 1.3   (8/06/92) -- Read the file list filename from AREA.DAT
  221.                            instead of assuming FILES.BBS.
  222. Version 1.2   (8/02/92) -- Added days header for new files list.
  223. Version 1.1   (7/30/92) -- Added file age cutoff.
  224. Version 1.0   (6/30/92) -- Original release.  Written in Borland C.
  225.  
  226. Large memory model
  227. */
  228.  
  229.  
  230. #include <ctype.h>
  231. #include <dos.h>
  232. #include <fcntl.h>
  233. #include <io.h>
  234. #include <share.h>
  235. #include <stdio.h>
  236. #include <stdlib.h>
  237. #include <string.h>
  238. #include <sys\stat.h>
  239. #include <time.h>
  240. #include <mstruct.h>  /*  Maximus structures by Scott J. Dudley  */
  241.  
  242.  
  243. #define MAXPATH     128
  244. #define MAXLINE     256
  245. #define MAXDESC     4096
  246.  
  247.  
  248. struct _arealist
  249.     {
  250.     char    name[40];
  251.     char    info[80];
  252.     char    path[80];
  253.     char    filesbbs[80];
  254.     sword   priv;
  255.     dword   locks;
  256.     byte    barricade;
  257.     struct _arealist *next;
  258.     };
  259.  
  260.  
  261. typedef struct _arealist __arealist;
  262. typedef struct _area __area;
  263.  
  264.  
  265. void process_args(int argc, char *argv[]);
  266. sword get_priv(char priv);
  267. dword get_keys(char *key_list);
  268. __arealist *read_areas(void);
  269. void process_header(char type, char *hfile, char *name, char *info, FILE *out);
  270. void process_entry(char *entry, char *path, char *name, char *info,
  271.       int *header, FILE *out);
  272.  
  273.  
  274. char    areafile[MAXPATH] = {"area.dat"};
  275. char    hareafile[MAXPATH] = {""};
  276. char    hbeginfile[MAXPATH] = {""};
  277. char    hendfile[MAXPATH] = {""};
  278. char    outfile[MAXPATH] = {"flist.txt"};
  279. sword   priv = NORMAL;
  280. dword   keys = 0x00000000;
  281. int     barricade = 0;
  282. int     comment = 0;
  283. long    days = -1;
  284. int     offline = 0;
  285. int     wrap = 2;
  286. int     quiet = 0;
  287.  
  288. long    age = -1;
  289.  
  290.  
  291. int main(int argc, char *argv[])
  292.     {
  293.  
  294.     char        filespath[MAXPATH];
  295.  
  296.     int         header;
  297.  
  298.     FILE        *out;
  299.     FILE        *files;
  300.  
  301.     __arealist  *areas;
  302.     __arealist  *cur;
  303.     __arealist  *prev;
  304.  
  305.     char        line[MAXDESC];
  306.  
  307.  
  308.     printf("FLIST 2.1 -- Copyright 1992 by Bob Quinlan (8/26/92)\n\n");
  309.  
  310.     process_args(argc, argv);
  311.  
  312.     areas = read_areas();
  313.  
  314.     if ((out = _fsopen(outfile, "wt", SH_DENYWR)) == NULL)
  315.         {
  316.         fprintf(stderr, "Unable to open %s\n", outfile);
  317.         exit(4);
  318.         }
  319.  
  320.     if (hbeginfile[0] != '\0')
  321.         process_header('b', hbeginfile, NULL, NULL, out);
  322.  
  323.     cur = areas;
  324.     while (cur != NULL)
  325.         {
  326.         if ((priv >= (cur->priv)) && (((~keys)&(cur->locks)) == 0) &&
  327.               ((cur->barricade == 0) || (barricade)))
  328.             {
  329.             if (!quiet)
  330.                 printf("%s -- %s", cur->name, cur->info);
  331.             strcpy(filespath, cur->filesbbs);
  332.             if (filespath[0] == '\0')
  333.                 {
  334.                 strcpy(filespath, cur->path);
  335.                 strcat(filespath, "files.bbs");
  336.                 }
  337.             header = 0;
  338.             if ((files = _fsopen(filespath, "rt", SH_DENYNONE)) != NULL)
  339.                 {
  340.                 while (fgets(line, MAXDESC, files) != NULL)
  341.                     {
  342.                     process_entry(line, cur->path, cur->name, cur->info,
  343.                           &header, out);
  344.                     if (!quiet)
  345.                         printf(".");
  346.                     }
  347.                 fclose(files);
  348.                 }
  349.             if (!quiet)
  350.                 printf("\n");
  351.             }
  352.         /*  Increment to next link and free previous link  */
  353.         prev = cur;
  354.         cur = cur->next;
  355.         free(prev);
  356.         }
  357.  
  358.     if (hendfile[0] != '\0')
  359.         process_header('e', hendfile, NULL, NULL, out);
  360.  
  361.     fclose(out);
  362.  
  363.     return 0;
  364.     }
  365.  
  366.  
  367. void process_args(int argc, char *argv[])
  368.     {
  369.     int i;
  370.  
  371.     for (i=1; i<argc; i++)
  372.         {
  373.         if ((argv[i][0] == '/') || (argv[i][0] == '-'))
  374.             {
  375.             switch (tolower(argv[i][1]))
  376.                 {
  377.                 case 'a':  /*  area.dat  */
  378.                     strncpy(areafile, argv[i]+2, MAXPATH);
  379.                     break;
  380.                 case 'b':  /*  include barricaded areas  */
  381.                     barricade = 1;
  382.                     break;
  383.                 case 'c':  /*  include file list comment lines  */
  384.                     comment = 1;
  385.                     break;
  386.                 case 'd':  /*  days (of file date)  */
  387.                     days = atol(argv[i]+2);
  388.                     age = time(NULL)-(days*24*60*60);
  389.                     break;
  390.                 case 'f':  /*  output file  */
  391.                     strncpy(outfile, argv[i]+2, MAXPATH);
  392.                     break;
  393.                 case 'h':  /*  headers  */
  394.                     switch(tolower(argv[i][2]))
  395.                         {
  396.                         case 'a':
  397.                             strncpy(hareafile, argv[i]+3, MAXPATH);
  398.                             break;
  399.                         case 'b':
  400.                             strncpy(hbeginfile, argv[i]+3, MAXPATH);
  401.                             break;
  402.                         case 'e':
  403.                             strncpy(hendfile, argv[i]+3, MAXPATH);
  404.                             break;
  405.                         default:
  406.                             fprintf(stderr, "Unknown switch: %s\n", argv[i]);
  407.                             exit(1);
  408.                             break;
  409.                         }
  410.                     break;
  411.                 case 'k':  /*  keys  */
  412.                     keys |= get_keys(argv[i]+2);
  413.                     break;
  414.                 case 'n':  /*  no-wrap  */
  415.                     wrap = 0;
  416.                     break;
  417.                 case 'o':  /*  offline files allowed  */
  418.                     offline = 1;
  419.                     break;
  420.                 case 'p':  /*  privilege  */
  421.                     priv = get_priv(argv[i][2]);
  422.                     if (priv < TWIT)
  423.                         {
  424.                         priv = TWIT;
  425.                         fprintf(stderr, "Invalid privilege: %c\n", argv[i][2]);
  426.                         }
  427.                     break;
  428.                 case 'q':  /*  quiet  */
  429.                     quiet = 1;
  430.                     break;
  431.                 case 't':  /*  truncate lines  */
  432.                     wrap = 1;
  433.                     break;
  434.                 default:
  435.                     fprintf(stderr, "Unknown switch: %s\n", argv[i]);
  436.                     exit(1);
  437.                     break;
  438.                 }
  439.             }
  440.         else
  441.             {
  442.             fprintf(stderr, "Not a switch: %s\n", argv[i]);
  443.             exit(1);
  444.             }
  445.         }
  446.     }
  447.  
  448.  
  449. sword get_priv(char priv)
  450.     {
  451.     char    priv_name[] = {"tdlnwpfecash"};
  452.     sword   priv_code[] =
  453.                 {
  454.                 TWIT,
  455.                 DISGRACE,
  456.                 LIMITED,
  457.                 NORMAL,
  458.                 WORTHY,
  459.                 PRIVIL,
  460.                 FAVORED,
  461.                 EXTRA,
  462.                 CLERK,
  463.                 ASSTSYSOP,
  464.                 SYSOP,
  465.                 HIDDEN
  466.                 };
  467.  
  468.     char    *where;
  469.  
  470.     where = strchr(priv_name, tolower(priv));
  471.     if (where == NULL)
  472.         return TWIT-1;
  473.  
  474.     return priv_code[(int)(where-priv_name)];
  475.     }
  476.  
  477.  
  478. dword get_keys(char *key_list)
  479.     {
  480.     char    *key;
  481.     dword   mask = 0x00000000;
  482.     int     keylen;
  483.     int     i;
  484.  
  485.     key = strupr(strdup(key_list));
  486.  
  487.     keylen = strlen(key);
  488.     for (i=0; i<keylen; i++)
  489.         {
  490.         if (key[i] == '*')
  491.             {
  492.             mask = 0xFFFFFFFF;
  493.             break;
  494.             }
  495.         else if ((key[i] >= '1') && (key[i] <= '8'))
  496.             mask |= 0x00000001L<<(key[i]-'1');
  497.         else if ((key[i] >= 'A') && (key[i] <= 'X'))
  498.             mask |= 0x00000100L<<(key[i]-'A');
  499.         }
  500.  
  501.     free(key);
  502.  
  503.     return mask;
  504.     }
  505.  
  506.  
  507. __arealist *read_areas(void)
  508.     {
  509.     FILE        *areadat;
  510.     __area      area;
  511.     __arealist  *tmp;
  512.     __arealist  *first;
  513.     __arealist  *cur = NULL;
  514.     dword       slen;
  515.     dword       offset = 0;
  516.  
  517.     if ((areadat = _fsopen(areafile, "rb", SH_DENYNONE)) == NULL)
  518.         {
  519.         fprintf(stderr, "Unable to open %s\n", areafile);
  520.         exit(2);
  521.         }
  522.  
  523.     while (fread(&area, sizeof(__area), 1, areadat) > 0)
  524.         {
  525.         if (offset == 0)
  526.             slen = (dword)(area.struct_len);
  527.         if (area.filepath[0] != '\0')
  528.             {
  529.             if ((tmp = (__arealist *)malloc(sizeof(__arealist))) == NULL)
  530.                 {
  531.                 fprintf(stderr, "Out of memory!\n");
  532.                 exit(5);
  533.                 }
  534.             if (cur == NULL)
  535.                 first = tmp;
  536.             else
  537.                 cur->next = tmp;
  538.             cur = tmp;
  539.             strcpy(cur->name, area.name);
  540.             strcpy(cur->info, area.fileinfo);
  541.             strcpy(cur->path, area.filepath);
  542.             strcpy(cur->filesbbs, area.filesbbs);
  543.             cur->priv = area.filepriv;
  544.             cur->locks = area.filelock;
  545.             cur->barricade = (area.filebar[0] != '\0');
  546.             }
  547.         offset += slen;
  548.         if (fseek(areadat, offset, SEEK_SET) != 0)
  549.             break;
  550.         }
  551.  
  552.     cur->next = NULL;
  553.     fclose(areadat);
  554.  
  555.     return first;
  556.     }
  557.  
  558.  
  559. void process_header(char type, char *hfile, char *name, char *info, FILE *out)
  560.     {
  561.     FILE    *h;
  562.     char    buf[MAXLINE];
  563.     char    tbuf[MAXLINE];
  564.     char    filler[MAXLINE];
  565.     int     flen;
  566.     char    *param;
  567.     int     plen;
  568.     int     fwidth;
  569.     char    *stopper;
  570.     char    c;
  571.     int     j, k;
  572.  
  573.     if ((h = _fsopen(hfile, "rt", SH_DENYNONE)) == NULL)
  574.         {
  575.         fprintf(stderr, "Unable to open %s\n", hfile);
  576.         exit(3);
  577.         }
  578.     /*  Read data from the header file  */
  579.     while (fgets(buf, MAXLINE, h) != NULL)
  580.         {
  581.         /*  Process special replacement characters  */
  582.         while ((param = strchr(buf, '%')) != NULL)
  583.             {
  584.             filler[0] = '\0';
  585.             plen = 2;
  586.             /*  Check the basic parameter type  */
  587.             switch (tolower(param[1]))
  588.                 {
  589.                 case 'a':  /*  age  */
  590.                     itoa(days, filler, 10);
  591.                     break;
  592.                 case 'd':  /*  date  */
  593.                     _strdate(filler);
  594.                     break;
  595.                 case 't':  /*  time  */
  596.                     _strtime(filler);
  597.                     break;
  598.                 }
  599.             /*  Only allow name and information in area headers  */
  600.             if (type == 'a')
  601.                 {
  602.                 switch (tolower(param[1]))
  603.                     {
  604.                     case 'n':  /*  name  */
  605.                         strcpy(filler, name);
  606.                         break;
  607.                     case 'i':  /*  information  */
  608.                         strcpy(filler, info);
  609.                         break;
  610.                     }
  611.                 }
  612.  
  613.             /*  If any replacements were found...  */
  614.             if (filler[0] != '\0')
  615.                 {
  616.                 /*  Process width values if present  */
  617.                 c = tolower(param[plen]);
  618.                 if (strchr("lrc", c) != NULL)
  619.                     {
  620.                     ++plen;
  621.                     /*  Convert the width value and find where it stops  */
  622.                     fwidth = (int)strtol(param+plen, &stopper, 10);
  623.                     plen += (int)(stopper-(param+plen));
  624.                     /*  If the field is wider than the filler string...  */
  625.                     if (fwidth >= MAXLINE)
  626.                         {
  627.                         fprintf(stderr, "Width %d too wide!  Ignored.\n",
  628.                               fwidth);
  629.                         }
  630.                     /*  If the field is wider than the current data...  */
  631.                     else if (strlen(filler) < fwidth)
  632.                         {
  633.                         switch (c)
  634.                             {
  635.                             case 'l':  /*  left justify  */
  636.                                 /*  Pad on the right  */
  637.                                 for (j=strlen(filler); j<fwidth; j++)
  638.                                     filler[j] = ' ';
  639.                                 break;
  640.                             case 'r':  /*  right justify  */
  641.                                 flen = strlen(filler);
  642.                                 /*  Shift the string right  */
  643.                                 for (j=1; j<=flen; j++)
  644.                                     filler[fwidth-j] = filler[flen-j];
  645.                                 /*  Pad on the left  */
  646.                                 for (j=0; j<fwidth-flen; j++)
  647.                                     filler[j] = ' ';
  648.                                 break;
  649.                             case 'c':  /*  center  */
  650.                                 flen = strlen(filler);
  651.                                 /*  Pad on the right  */
  652.                                 k = (fwidth-flen+1)/2;
  653.                                 for (j=1; j<=k; j++)
  654.                                     filler[fwidth-j] = ' ';
  655.                                 /*  Shift the string to center  */
  656.                                 for (j=1; j<=flen; j++)
  657.                                     filler[fwidth-k-j] = filler[flen-j];
  658.                                 /*  Pad on the left  */
  659.                                 k = (fwidth-flen)/2;
  660.                                 for (j=0; j<k; j++)
  661.                                     filler[j] = ' ';
  662.                                 break;
  663.                             }
  664.                         /*  Terminate the adjusted string  */
  665.                         filler[fwidth] = '\0';
  666.                         }
  667.                     }
  668.  
  669.                 /*  Process fill character if present  */
  670.                 if (tolower(param[plen]) == 'f')
  671.                     {
  672.                     strset(filler, param[++plen]);
  673.                     ++plen;
  674.                     }
  675.                 /*  Skip "no fill" parameter if present  */
  676.                 else if (strncmpi(param+plen, "nf", 2) == 0)
  677.                     {
  678.                     plen += 2;
  679.                     }
  680.  
  681.                 /*  Perform the replacment  */
  682.                 if ((strlen(buf)+strlen(filler)-plen) >= MAXLINE)
  683.                     {
  684.                     fprintf(stderr, "String overflow!\n");
  685.                     exit(6);
  686.                     }
  687.                 param[0] = '\0';
  688.                 strcpy(tbuf, buf);
  689.                 strcat(tbuf, filler);
  690.                 strcat(tbuf, param+plen);
  691.                 strcpy(buf, tbuf);
  692.                 }
  693.             }
  694.  
  695.         /*  Output header  */
  696.         fputs(buf, out);
  697.         }
  698.  
  699.     fclose(h);
  700.     }
  701.  
  702.  
  703. void process_entry(char *entry, char *path, char *name, char *info,
  704.       int *header, FILE *out)
  705.     {
  706.     char    filepath[MAXPATH];
  707.     int     file;
  708.  
  709.     long            fsize;
  710.     struct ftime    ftimep;
  711.     struct tm         ftm;
  712.     int             fdata;
  713.  
  714.     char    line[MAXDESC];
  715.     char    fill[40];
  716.     char    *descrip;
  717.     char    *end;
  718.     char    *ch;
  719.     int     i;
  720.  
  721.     strcpy(line, entry);
  722.  
  723.     /*  Trim newline  */
  724.     end = strchr(line, '\n');
  725.     if (end != NULL)
  726.         end[0] = '\0';
  727.     else
  728.         end = strchr(line, '\0');
  729.  
  730.     /*  Check for comment lines  */
  731.     if (strchr(" -", line[0]) != NULL)
  732.         {
  733.         if (comment)
  734.             {
  735.             /*  Output area header if not already done  */
  736.             if (!(*header))
  737.                 {
  738.                 if (hareafile[0] != '\0')
  739.                     process_header('a', hareafile, name, info, out);
  740.                 else
  741.                     fprintf(out, "\n\n    Area %s -- %s\n\n", name, info);
  742.                 *header = 1;
  743.                 }
  744.  
  745.             /*  Output comment line  */
  746.             fprintf(out, "%s\n", line);
  747.             }
  748.         }
  749.     else
  750.         {
  751.         /*  Parse into filename and description, skipping switches  */
  752.         descrip = line;
  753.         do
  754.             {
  755.             ch = strpbrk(descrip, " \t");
  756.             if (ch == NULL)
  757.                 descrip = end;
  758.             else
  759.                 {
  760.                 ch[0] = '\0';
  761.                 i = strspn(++ch, " \t");
  762.                 descrip = ch+i;
  763.                 }
  764.             } while (descrip[0] == '/');
  765.  
  766.         /*  Get file size and date  */
  767.         strcpy(filepath, path);
  768.         strcat(filepath, line);
  769.         if ((file = sopen(filepath, O_RDONLY, SH_DENYNONE)) == -1)
  770.             {
  771.             if (offline)
  772.                 fdata = 0;
  773.             else
  774.                 return;
  775.             }
  776.         else
  777.             {
  778.             fsize = filelength(file);
  779.             getftime(file, &ftimep);
  780.             close(file);
  781.             fdata = 1;
  782.  
  783.             memset(&ftm, 0, sizeof(struct tm));
  784.             ftm.tm_sec = ftimep.ft_tsec*2;
  785.             ftm.tm_min = ftimep.ft_min;
  786.             ftm.tm_hour = ftimep.ft_hour;
  787.             ftm.tm_mday = ftimep.ft_day;
  788.             ftm.tm_mon = ftimep.ft_month-1;
  789.             ftm.tm_year = ftimep.ft_year+80;
  790.             if (mktime(&ftm) < age)
  791.                 return;
  792.             }
  793.  
  794.         /*  Output area header if not already done  */
  795.         if (!(*header))
  796.             {
  797.             if (hareafile[0] != '\0')
  798.                 process_header('a', hareafile, name, info, out);
  799.             else
  800.                 fprintf(out, "\n\n    Area %s -- %s\n\n", name, info);
  801.             *header = 1;
  802.             }
  803.  
  804.         /*  Output file entry  */
  805.         if (fdata)
  806.             {
  807.             fprintf(out, "%-12s %7lu %2.2u-%2.2u-%2.2u", line,
  808.                   fsize, ftimep.ft_month, ftimep.ft_day,
  809.                   (ftimep.ft_year+80)%100);
  810.             }
  811.         else
  812.             {
  813.             fprintf(out, "%-12s                 ", line);
  814.             }
  815.         /*  Output description  */
  816.         switch (wrap)
  817.             {
  818.             case 0:  /*  no-wrap  */
  819.                 fprintf(out, " %s\n", descrip);
  820.                 break;
  821.             case 1:  /*  truncate  */
  822.                 fprintf(out, " %.49s\n", descrip);
  823.                 break;
  824.             case 2:  /*  wrap  */
  825.                 strcpy(fill, " ");
  826.                 while (strlen(descrip) > 49)
  827.                     {
  828.                     for (i=49; i>20; i--)
  829.                         {
  830.                         if (strchr(" \t", descrip[i]) != NULL)
  831.                             break;
  832.                         }
  833.                     /*  If there are no breaks...  */
  834.                     if (i == 20)
  835.                         {
  836.                         fprintf(out, "%s%.49s\n", fill, descrip);
  837.                         descrip += 49;
  838.                         }
  839.                     else
  840.                         {
  841.                         descrip[i++] = '\0';
  842.                         fprintf(out, "%s%.49s\n", fill, descrip);
  843.                         descrip += i;
  844.                         while (strchr(" \t", descrip[0]) != NULL)
  845.                             descrip++;
  846.                         }
  847.                     strcpy(fill, "                              ");
  848.                     }
  849.                 fprintf(out, "%s%.49s\n", fill, descrip);
  850.                 break;
  851.             }
  852.         }
  853.     }
  854.  
  855.  
  856.